{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 1. 在测试视频(OpenCV安装目录\\sources\\samples\\data)上，使用基于混合高斯模型的背景提取算法，提取前景并显示(显示二值化图像，前景为白色)。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# -*- coding:utf8 -*-\n",
    "import cv2 as cv\n",
    "import copy\n",
    "\n",
    "video_file_name = \"videos/vtest.avi\"\n",
    "cap = cv.VideoCapture(video_file_name)\n",
    "fgbg = cv.createBackgroundSubtractorMOG2()\n",
    "thresh = 200\n",
    "\n",
    "count = 0\n",
    "while True:\n",
    "    ret, frame = cap.read()\n",
    "    if ret is not True:\n",
    "        break\n",
    "    \n",
    "    fgmask = fgbg.apply(frame)\n",
    "    bg_image = fgbg.getBackgroundImage()\n",
    "    \n",
    "    count = count + 1\n",
    "    if (count % 100) > 0:\n",
    "        continue\n",
    "    \n",
    "    fgmask_copy = copy.deepcopy(fgmask)\n",
    "    \n",
    "    _, contours, _ = cv.findContours(fgmask_copy,\n",
    "                                     cv.RETR_EXTERNAL,\n",
    "                                     cv.CHAIN_APPROX_SIMPLE)\n",
    "    contour_image = cv.drawContours(fgmask_copy, \n",
    "                                    contours, -1, (0, 0, 255), 1)\n",
    "    cv.imshow(\"Frame-{} Foreground\".format(count), contour_image)\n",
    "    cv.imshow(\"Frame-{} Background\".format(count), bg_image)\n",
    "\n",
    "    cv.waitKey()\n",
    "    cv.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 运行结果\n",
    "![frame-100](https://gitee.com/coolhenry/cv-week-3/raw/master/out/frame_100.png)\n",
    "![frame-100](https://gitee.com/coolhenry/cv-week-3/raw/master/out/frame_200.png)\n",
    "![frame-100](https://gitee.com/coolhenry/cv-week-3/raw/master/out/frame_300.png)\n",
    "#### 结果分析\n",
    "分别截取了测试视频第100帧，200帧，300帧的背景提取结果，可以看到对于缓慢运动的目标，检测效果比较稳定。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 在1基础上，将前景目标进行分割，进一步使用不同颜色矩形框标记，并在命令行窗口中输出每个矩形框的位置和大小。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# -*- coding:utf8 -*-\n",
    "import cv2 as cv\n",
    "import copy\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "cap = cv.VideoCapture(\"videos/vtest.avi\")\n",
    "fgbg = cv.createBackgroundSubtractorMOG2()\n",
    "thresh = 200\n",
    "\n",
    "\n",
    "def reverse(img):\n",
    "    b, g, r = cv.split(img)\n",
    "    return cv.merge([r, g, b])\n",
    "\n",
    "\n",
    "frameId = 0\n",
    "while True:\n",
    "    ret, frame = cap.read()\n",
    "    if ret is not True:\n",
    "        break\n",
    "    \n",
    "    fgmask = fgbg.apply(frame)\n",
    "    bg_image = fgbg.getBackgroundImage()\n",
    "        \n",
    "    frameId = frameId + 1\n",
    "    if (frameId % 100) > 0:\n",
    "        continue\n",
    "\n",
    "    fgmask_copy = copy.deepcopy(fgmask)\n",
    "    fgmask_copy = cv.medianBlur(fgmask_copy, 5)\n",
    "    cv.threshold(fgmask_copy, 127, 255, cv.THRESH_BINARY, fgmask_copy)\n",
    "    \n",
    "    _, contours, _ = cv.findContours(fgmask_copy, \n",
    "                                     cv.RETR_EXTERNAL, \n",
    "                                     cv.CHAIN_APPROX_SIMPLE)\n",
    "    \n",
    "    plt.figure(num=frameId, \n",
    "               figsize=(2 * frame.shape[0] * 0.01, \n",
    "                        1 * frame.shape[1] * 0.01))\n",
    "    plt.subplot(1, 2, 1), plt.title(\"Foreground Image {}\".format(frameId)),\\\n",
    "        plt.imshow(fgmask_copy, \"gray\"), plt.xticks([]), plt.yticks([])\n",
    "    \n",
    "    print(\"=========第{}帧的检测结果=========\".format(frameId))\n",
    "    count = 0\n",
    "    for contour in contours:\n",
    "        area = cv.contourArea(contour)\n",
    "        if area < thresh:\n",
    "            continue\n",
    "        \n",
    "        count = count + 1\n",
    "        minRect = cv.minAreaRect(contour)\n",
    "        print(\"矩形:{}, 位置:[{}], 长宽:[{}]\".format(\n",
    "            count, np.int0(minRect[0]), np.int0(minRect[1])\n",
    "        ))\n",
    "        \n",
    "        box = cv.boxPoints(minRect)\n",
    "        box = np.int0(box)\n",
    "        \n",
    "        cv.drawContours(frame, [box], 0, (0, 0, 255), 2)\n",
    "    \n",
    "    plt.subplot(1, 2, 2), plt.title(\"Frame {}\".format(frameId)),\\\n",
    "        plt.imshow(reverse(frame)), plt.xticks([]), plt.yticks([])\n",
    "\n",
    "    plt.show()\n",
    "\n",
    "    print(\"=========第{}帧END=========\".format(frameId))\n",
    "        \n",
    "    if frameId >= 300:\n",
    "        break\n",
    "\n",
    "plt.close(\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 运行结果\n",
    "![frame-contours-100](https://gitee.com/coolhenry/cv-week-3/raw/master/out/frame_contour_100.png)\n",
    "![frame-contours-200](https://gitee.com/coolhenry/cv-week-3/raw/master/out/frame_contour_200.png)\n",
    "![frame-contours-300](https://gitee.com/coolhenry/cv-week-3/raw/master/out/frame_contour_300.png)\n",
    "=========第100帧的检测结果=========\n",
    "矩形:1, 位置:[[394 233]], 长宽:[[17 34]]\n",
    "矩形:2, 位置:[[356 234]], 长宽:[[21 73]]\n",
    "矩形:3, 位置:[[601 197]], 长宽:[[23 80]]\n",
    "矩形:4, 位置:[[511 183]], 长宽:[[49 69]]\n",
    "矩形:5, 位置:[[231  75]], 长宽:[[12 33]]\n",
    "=========第100帧END=========\n",
    "=========第200帧的检测结果=========\n",
    "矩形:1, 位置:[[625 311]], 长宽:[[27 95]]\n",
    "矩形:2, 位置:[[718 300]], 长宽:[[97 39]]\n",
    "矩形:3, 位置:[[237 228]], 长宽:[[24 75]]\n",
    "矩形:4, 位置:[[102 184]], 长宽:[[30 61]]\n",
    "矩形:5, 位置:[[506 181]], 长宽:[[24 74]]\n",
    "矩形:6, 位置:[[712 126]], 长宽:[[15 49]]\n",
    "矩形:7, 位置:[[687 125]], 长宽:[[21 54]]\n",
    "=========第200帧END=========\n",
    "=========第300帧的检测结果=========\n",
    "矩形:1, 位置:[[330 253]], 长宽:[[83 29]]\n",
    "矩形:2, 位置:[[628 194]], 长宽:[[67 20]]\n",
    "矩形:3, 位置:[[598 194]], 长宽:[[30 69]]\n",
    "矩形:4, 位置:[[305 194]], 长宽:[[68 25]]\n",
    "矩形:5, 位置:[[262 190]], 长宽:[[26 60]]\n",
    "矩形:6, 位置:[[194 199]], 长宽:[[28 77]]\n",
    "=========第300帧END========="
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. 使用光流估计方法，在前述测试视频上计算特征点，进一步进行特征点光流估计。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# -*- coding:utf8 -*-\n",
    "import cv2 as cv\n",
    "import numpy as np\n",
    "\n",
    "video_file_name = \"videos/vtest.avi\"\n",
    "cap = cv.VideoCapture(video_file_name)\n",
    "\n",
    "# 特征点检测参数\n",
    "feature_params = dict(maxCorners=100,\n",
    "                      qualityLevel=0.3,\n",
    "                      minDistance=7,\n",
    "                      blockSize=7)\n",
    "\n",
    "# L-K参数\n",
    "lk_params = dict(winSize=(15, 15),\n",
    "                 maxLevel=2,\n",
    "                 criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))\n",
    "\n",
    "# 创建颜色\n",
    "color = (0, 0, 255)\n",
    "\n",
    "# 获得第一帧的特征点\n",
    "ret, old_frame = cap.read()\n",
    "old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)\n",
    "p0 = cv.goodFeaturesToTrack(old_gray, mask=None, **feature_params)\n",
    "\n",
    "# 开始处理接下来每一帧\n",
    "frameId = 1\n",
    "while True:\n",
    "    ret, frame = cap.read()\n",
    "    if ret is not True:\n",
    "        break\n",
    "    \n",
    "    frameId = frameId + 1\n",
    "        \n",
    "    # 根据当前帧与上一帧的灰度图来计算光流\n",
    "    frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)\n",
    "    p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)\n",
    "    \n",
    "    # 选择好的跟踪点\n",
    "    good_new = p1[st == 1]\n",
    "    good_old = p0[st == 1]\n",
    "    \n",
    "    # 画出光流向量\n",
    "    frame_copy = frame.copy()\n",
    "    for i, (new, old) in enumerate(zip(good_new, good_old)):\n",
    "        a, b = new.ravel()\n",
    "        c, d = old.ravel()\n",
    "        cv.line(frame_copy, (a, b), (c, d), color)\n",
    "        cv.circle(frame_copy, (a, b), 3, color)\n",
    "    \n",
    "    # 更换当前帧为下一帧\n",
    "    old_gray = frame_gray.copy()\n",
    "    p0 = good_new.reshape(-1, 1, 2)\n",
    "    \n",
    "    if frameId % 100 == 0 and frameId < 400:\n",
    "        # 打印出当前帧的形象\n",
    "        cv.imshow(\"Optical Flow Frame {}\".format(frameId), frame_copy)\n",
    "    \n",
    "        # 接受Esc键退出\n",
    "        k = cv.waitKey(30) & 0xff\n",
    "        if k == 27:\n",
    "            break\n",
    "        else:\n",
    "            cv.waitKey()\n",
    "            cv.destroyAllWindows()\n",
    "    else:\n",
    "        cv.destroyAllWindows()\n",
    "\n",
    "cv.destroyAllWindows()\n",
    "cap.release()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 运行结果\n",
    "![frame-contours-100](https://gitee.com/coolhenry/cv-week-3/raw/master/out/optical_flow_100.png)\n",
    "![frame-contours-200](https://gitee.com/coolhenry/cv-week-3/raw/master/out/optical_flow_200.png)\n",
    "![frame-contours-300](https://gitee.com/coolhenry/cv-week-3/raw/master/out/optical_flow_300.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
